import Request from 'request-promise'
import fs from 'fs'
// import { resolve } from 'path'
import * as _ from 'lodash'
import * as util from './util'

const base = 'https://api.weixin.qq.com/cgi-bin/'
const api = {
  accessToken: base + 'token?grant_type=client_credential',
  temporary: {
    upload: base + 'media/upload?',
    fetch: base + 'media/get?'
  },
  permanent: {
    upload: base + 'material/add_material?',
    uploadNews: base + 'material/add_news?',
    uploadNewsPic: base + 'media/uploadimg?',
    fetch: base + 'material/get_material?',
    update: base + 'material/update_news?',
    del: base + 'material/del_material?',
    count: base + 'material/get_materialcount?',
    batch: base + 'material/batchget_material?'
  },
  tag: {
    create: base + 'tags/create?',
    fetch: base + 'tags/get?',
    update: base + 'tags/update?',
    del: base + 'tags/delete?',
    fetchUsers: base + 'user/tag/get?',
    batchTag: base + 'tags/members/batchtagging?',
    batchUnTag: base + 'tags/members/batchuntagging?',
    getTagList: base + 'tags/getidlist?'
  },
  user: {
    remark: base + 'user/info/updateremark?',
    info: base + 'user/info?',
    batchInfo: base + 'user/info/batchget?',
    fetchUserList: base + 'user/get?',
    fetchBlackList: base + 'tags/members/getblacklist?',
    batchBlackUsers: base + 'tags/members/batchblacklist?',
    batchUnBlackUsers: base + 'tags/members/batchunblacklist?'
  },
  menu: {
    create: base + 'menu/create?',
    get: base + 'menu/get?',
    del: base + 'menu/delete?',
    addconditional: base + 'menu/addconditional?',
    delconditional: base + 'menu/delconditional?',
    getInfo: base + 'get_current_selfmenu_info?'
  },
  ticket: {
    get: base + 'ticket/getticket?'
  }
}

// function statFile(filepath) {  // 读取文件大小
//   return new Promise((resolve, reject) => {
//     fs.stat(filepath, (err, stat) => {
//       if (err) reject(err)
//       else resolve(stat)
//     })
//   })
// }
export default class Wechat {
  constructor(opts) {
    this.opts = Object.assign({}, opts)

    this.appID = opts.appID
    this.appSecret = opts.appSecret
    this.getAccessToken = opts.getAccessToken
    this.saveAccessToken = opts.saveAccessToken

    this.getTicket = opts.getTicket
    this.saveTicket = opts.saveTicket

    this.fetchAccessToken()
  }

  async request(options) {
    options = Object.assign({}, options, {json: true})
    try {
      const response = await Request(options)
      return response
    } catch (error) {
      console.error('错误信息:' + error)
    }
  }

  async fetchAccessToken() {
    let data = await this.getAccessToken()   // 获取token
    if (!this.isValidToken(data, 'access_token')) {   // 验证token过期
      data = await this.updateAccessToken()   // 过期更新token
    }
    await this.saveAccessToken(data)    // 保存token
    return data
  }

  async fetchTicket(token) {
    let data = await this.getTicket()
    if (!this.isValidToken(data, 'ticket')) {
      data = await this.updateTicket(token)
    }
    await this.saveTicket(data)
    return data
  }

  async updateAccessToken() {
    const url = api.accessToken + '&appid=' + this.appID + '&secret=' + this.appSecret
    const data = await this.request({url: url})
    const now = (new Date().getTime())
    const expiresIn = now + (data.expires_in - 20) * 1000
    data.expires_in = expiresIn
    console.log('data.expires:' + data.expires_in)
    return data
  }

  async updateTicket(token) {
    const url = api.ticket.get + 'access_token=' + token + '&type=jsapi'
    const data = await this.request({url: url})

    const now = (new Date().getTime())
    const expiresIn = now + (data.expires_in - 20) * 1000
    data.expires_in = expiresIn
    console.log('data.expires:' + data.expires_in)
    return data
  }

  isValidToken(data, name) {
    if (!data || !data[name] || !data.expires_in) {
      console.log(name + '票据没有,直接退出')
      return false
    }
    console.log(data[name])

    const expiresIn = data.expires_in
    const now = (new Date().getTime())

    if (now < expiresIn) {
      console.log(name + '没过期')
      return true
    } else {
      console.log(name + '过期了')
      return false
    }
  }

  async handle(operation, ...args) {
    const tokenData = await this.fetchAccessToken()   // 再次取token
    const options = this[operation](tokenData.access_token, ...args)
    
    const data = await this.request(options)
    return data
  }
  uploadMaterial(token, type, material, permanent) {
    let form = {}
    let url = api.temporary.upload

    if (permanent) {
      url = api.permanent.upload

      _.extend(form, permanent)
    }

    if (type === 'pic') {
      url = api.permanent.uploadNewsPIC
    }
    if (type === 'news') {
      url = api.permanent.uploadNews
      form = material
    } else {
      form.media = fs.createReadStream(material)
      // const stat = await statFile(material)
      // form.file('media', material, path.basename(material), stat.size)
    }

    let uploadUrl = url + 'access_token=' + token

    if (!permanent) {
      uploadUrl += '&type=' + type
    } else {
      if (type !== 'news') {
        form.access_token = token
      }
      // form.field('access_token', token)
    }

    const options = {
      method: 'POST',
      url: uploadUrl,
      json: true
    }
    if (type === 'news') {
      options.body = form
    } else {
      options.formData = form
    }

    return options
  }

  fetchMaterial(token, mediaId, type, permanent) {
    let form = {}
    let fetchUrl = permanent ? api.permanent.fetch : api.temporary.fetch
    let url = fetchUrl + 'access_token=' + token
    let options = {method: 'POST', url: url}

    if (permanent) {
      form.media_id = mediaId
      form.access_token = token
      options.body = form
    } else {
      if (type === 'video') {
        url = url.replace('https://', 'http://')
      }
      url += '&media_id' + mediaId
    }

    return options
  }

  deleteMaterial(token, mediaId) {
    const form = {
      media_id: mediaId
    }
    const url = api.permanent.del + 'access_token=' + token + '&media_id=' + mediaId

    return {method: 'POST', url: url, body: form}
  }

  updateMaterial(token, mediaId, news) {
    const form = {
      media_id: mediaId
    }
    _.extend(form, news)
    const url = api.permanent.update + 'access_token=' + token + '&media_id=' + mediaId
    return {method: 'POST', url: url, body: form}
  }

  countMaterial(token) {
    const url = api.permanent.count + 'access_token=' + token

    return {method: 'POST', url: url}
  }

  batchMaterial(token, options) {
    options.type = options.type || 'image'
    options.offset = options.offset || 0
    options.count = options.count || 10
    const url = api.permanent.batch + 'access_token' + token

    return {method: 'POST', url: url, body: options}
  }

  createTag(token, name) {
    const form = {
      tag: {
        name: name
      }
    }
    const url = api.tag.create + 'access_token=' + token

    return {method: 'POST', url: url, body: form}
  }

  updateTag(token, tagId, name) {
    const form = {
      tag: {
        id: tagId,
        name: name
      }
    }
    const url = api.tag.update + 'access_token=' + token

    return {method: 'POST', url: url, body: form}
  }

  delTag(token, tagId, name) {
    const form = {
      tag: {
        id: tagId
      }
    }
    const url = api.tag.del + 'access_token=' + token

    return {method: 'POST', url: url, body: form}
  }

  fetchTagUsers(token, tagId, openId) {
    const form = {
      tagid: tagId,
      next_openid: openId || ''
    }

    const url = api.tag.fetchUsers + 'access_token=' + token

    return {method: 'POST', url: url, body: form}
  }

  // unTag true||false
  batchTag(token, tagId, openidList, unTag) {
    const form = {
      openid_list: openidList,
      tagid: tagId
    }

    let url = unTag ? api.tag.batchUnTag : api.tag.batchTag
    url += 'access_token=' + token
    return {method: 'POST', url: url, body: form}
  }

  getTagList(token, openId) {
    const form = {
      openid: openId
    }
    const url = api.tag.getTagList + 'access_token=' + token

    return {method: 'POST', url: url, body: form}
  }

  remarkUser(token, openId, remark) {
    const form = {
      openid: openId,
      remark: remark
    }
    const url = api.user.remark + 'access_token=' + token

    return {method: 'POST', url: url, body: form}
  }

  getUserInfo(token, openId, lang = 'zh_CN') {
    const url = api.user.info + 'access_token=' + token + '&openid=' + openId + '&lang=' + lang

    return {url: url}
  }

  batchUserInfo(token, userList) {
    const form = {
      user_list: userList
    }
    const url = api.user.batchInfo + 'access_token=' + token

    return {method: 'POST', url: url, body: form}
  }

  fetchUserList(token, openId = '') {
    const url = api.user.fetchUserList + 'access_token=' + token + '&next_openid=' + openId

    return {url: url}
  }

  // 微信菜单接口
  createMenu(token, menu) {
    const url = api.menu.create + 'access_token=' + token
    return {method: 'POST', url: url, body: menu}
  }

  getMenu(token) {
    const url = api.menu.get + 'access_token=' + token
    return {url: url}
  }

  deleteMenu(token) {
    const url = api.menu.del + 'access_token=' + token
    return {url: url}
  }

  addConditionMenu(token, menu, rule) {
    const form = {
      button: menu,
      matchrule: rule
    }
    const url = api.menu.addconditional + 'access_token=' + token
    return {method: 'POST', url: url, body: form}
  }

  delConditionMenu(token, menuId) {
    const form = {
      menuid: menuId
    }
    const url = api.menu.delconditional + 'access_token=' + token
    return {method: 'POST', url: url, body: form}
  }
  getCurrentMenuInfo(token) {
    const url = api.menu.getInfo + 'access_token=' + token
    return {url: url}
  }
  sign(ticket, url) {
    return util.sign(ticket, url)
  }
}
